package com.kexio.core.security.aspect;

import com.kexio.common.exception.BusinessException;
import com.kexio.core.security.annotation.RequiresPermission;
import com.kexio.core.security.annotation.RequiresRole;
import com.kexio.core.security.context.UserContext;
import com.kexio.core.security.context.UserContextHolder;
import com.kexio.core.security.service.RbacService;
import java.lang.reflect.Method;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * 权限检查AOP切面
 * 处理@RequiresPermission和@RequiresRole注解的权限检查
 *
 * @author Kexio Team
 * @since 1.0.0
 */
@Aspect
@Component
@Order(100) // 设置切面执行顺序，数值越小优先级越高
public class PermissionCheckAspect {

    private static final Logger log = LoggerFactory.getLogger(PermissionCheckAspect.class);

    @Autowired
    private RbacService rbacService;

    /**
     * 定义切入点：所有标注了@RequiresPermission注解的方法
     */
    @Pointcut("@annotation(com.kexio.core.security.annotation.RequiresPermission) || " +
              "@within(com.kexio.core.security.annotation.RequiresPermission)")
    public void requiresPermissionPointcut() {}

    /**
     * 定义切入点：所有标注了@RequiresRole注解的方法
     */
    @Pointcut("@annotation(com.kexio.core.security.annotation.RequiresRole) || " +
              "@within(com.kexio.core.security.annotation.RequiresRole)")
    public void requiresRolePointcut() {}

    /**
     * 权限检查切面
     */
    @Around("requiresPermissionPointcut()")
    public Object checkPermission(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        
        // 首先检查方法级别的注解
        RequiresPermission methodAnnotation = AnnotationUtils.findAnnotation(method, RequiresPermission.class);
        RequiresPermission annotation = methodAnnotation;
        
        // 如果方法上没有注解，检查类级别的注解
        if (annotation == null) {
            annotation = AnnotationUtils.findAnnotation(method.getDeclaringClass(), RequiresPermission.class);
        }

        if (annotation == null) {
            // 没有注解，直接继续执行
            return joinPoint.proceed();
        }

        // 如果注解被禁用，跳过检查
        if (annotation.disabled()) {
            log.debug("权限检查已禁用: {}.{}", method.getDeclaringClass().getSimpleName(), method.getName());
            return joinPoint.proceed();
        }

        // 检查用户是否已认证
        UserContext userContext = UserContextHolder.getContext();
        if (userContext == null) {
            log.warn("用户未登录，访问被拒绝: {}.{}", method.getDeclaringClass().getSimpleName(), method.getName());
            throw new BusinessException(401, "用户未登录");
        }

        // 超级管理员拥有所有权限
        if (userContext.isAdmin()) {
            log.debug("超级管理员访问: {}.{}", method.getDeclaringClass().getSimpleName(), method.getName());
            return joinPoint.proceed();
        }

        // 执行权限检查
        String[] permissions = annotation.value();
        RequiresPermission.Logical logical = annotation.logical();
        boolean hasPermission;

        if (logical == RequiresPermission.Logical.AND) {
            hasPermission = rbacService.hasAllPermissions(permissions);
        } else {
            hasPermission = rbacService.hasAnyPermission(permissions);
        }

        if (!hasPermission) {
            String message = annotation.message();
            if (message.isEmpty()) {
                message = String.format("缺少必要权限: %s", String.join(", ", permissions));
            }
            
            log.warn("权限不足，访问被拒绝: user={}, method={}.{}, required={}, logical={}", 
                    userContext.getUsername(), 
                    method.getDeclaringClass().getSimpleName(), 
                    method.getName(),
                    String.join(",", permissions),
                    logical);
            
            throw new BusinessException(403, message);
        }

        log.debug("权限检查通过: user={}, method={}.{}", 
                userContext.getUsername(), 
                method.getDeclaringClass().getSimpleName(), 
                method.getName());

        return joinPoint.proceed();
    }

    /**
     * 角色检查切面
     */
    @Around("requiresRolePointcut()")
    public Object checkRole(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        
        // 首先检查方法级别的注解
        RequiresRole methodAnnotation = AnnotationUtils.findAnnotation(method, RequiresRole.class);
        RequiresRole annotation = methodAnnotation;
        
        // 如果方法上没有注解，检查类级别的注解
        if (annotation == null) {
            annotation = AnnotationUtils.findAnnotation(method.getDeclaringClass(), RequiresRole.class);
        }

        if (annotation == null) {
            // 没有注解，直接继续执行
            return joinPoint.proceed();
        }

        // 如果注解被禁用，跳过检查
        if (annotation.disabled()) {
            log.debug("角色检查已禁用: {}.{}", method.getDeclaringClass().getSimpleName(), method.getName());
            return joinPoint.proceed();
        }

        // 检查用户是否已认证
        UserContext userContext = UserContextHolder.getContext();
        if (userContext == null) {
            log.warn("用户未登录，访问被拒绝: {}.{}", method.getDeclaringClass().getSimpleName(), method.getName());
            throw new BusinessException(401, "用户未登录");
        }

        // 超级管理员拥有所有角色
        if (userContext.isAdmin()) {
            log.debug("超级管理员访问: {}.{}", method.getDeclaringClass().getSimpleName(), method.getName());
            return joinPoint.proceed();
        }

        // 执行角色检查
        String[] roles = annotation.value();
        RequiresRole.Logical logical = annotation.logical();
        boolean hasRole;

        if (logical == RequiresRole.Logical.AND) {
            hasRole = true;
            for (String role : roles) {
                if (!rbacService.hasRole(role)) {
                    hasRole = false;
                    break;
                }
            }
        } else {
            hasRole = rbacService.hasAnyRole(roles);
        }

        if (!hasRole) {
            String message = annotation.message();
            if (message.isEmpty()) {
                message = String.format("缺少必要角色: %s", String.join(", ", roles));
            }
            
            log.warn("角色不足，访问被拒绝: user={}, method={}.{}, required={}, logical={}", 
                    userContext.getUsername(), 
                    method.getDeclaringClass().getSimpleName(), 
                    method.getName(),
                    String.join(",", roles),
                    logical);
            
            throw new BusinessException(403, message);
        }

        log.debug("角色检查通过: user={}, method={}.{}", 
                userContext.getUsername(), 
                method.getDeclaringClass().getSimpleName(), 
                method.getName());

        return joinPoint.proceed();
    }
}
